CommonJSとES Modulesについてまとめる | 您所在的位置:网站首页 › Modules CommonJS modules Nodejs v2230 › CommonJSとES Modulesについてまとめる |
モチベーション
普段フロントエンドを領分にしているのになかなかこのあたりの基礎が足りていないと感じることが多いので、なんとかしたい。 ES Modules方式でしか対応されていないライブラリを使おうとしてコケたので色々調べたのも含め、まとめていく。 ちなみにその辺りについてはこの神記事見ると良い。 https://blog.cybozu.io/entry/2020/10/06/170000 個人的に気になっているモジュールシステムについて掘り下げていく。 CommonJSCommonJSとは、サーバーサイドなどのウェブブラウザ環境外におけるJavaScriptの各種仕様を定めることを目標としたプロジェクトである。 from Wikipedia 例えばNode.jsで使われている。 Node.jsはデフォルトで全てのモジュールをCommonJSで扱うが、Node.jsは最近のバージョンでES Modulesに対応するなどしていて、潮流はES Modulesに流れつつある。 https://github.com/nodejs/node-v0.x-archive/issues/5132#issuecomment-15432598 後述するが、それでもまだ捨てきれない理由がある。 モジュールNode.jsはデフォルトで全てのモジュールをCommonJSで扱う。 https://nodejs.org/api/modules.html#modules-commonjs-modules 他モジュールを呼び出す際はrequireで呼び出す。 main.js const functions = require('./libs/functions') functions.hoge() ES ModulesECMA Script Modulesの略。 ECMAScript(エクマスクリプト)は、Ecma Internationalのもとで標準化手続きが行われているJavaScriptの規格 from wikipedia ウェブブラウザがECMAScriptをサポートしている。 モジュールNode.jsはデフォルトで全てのモジュールをCommonJSで扱うので、以下のいずれかの対応でモジュールシステムを変える必要がある。 方法1. package.jsonを追加し、"type": "module"を設定する。mainを設定する必要がある。 package.json { "type": "module", "main": "./main.js" }方法2.--input-type=moduleをつけて実行する node main.js --input-type=module方法3. .mjsに拡張子を変える main.mjs const functions = require('./libs/functions.mjs') functions.hoge()参考元:https://nodejs.org/api/esm.html#enabling 互換性について CommonJSのモジュールからESMを呼ぶ CommonJS/main.js import { hello } from './libs/functions.js' hello() const functions = require('ESModules/libs/functions.js') functions.hello() ❯ node main.js 1234 /Users/yodaka/Desktop/jsStudy/commonJS/main.js:3 const functions2 = require('../ESModules/libs/functions.js') ^ Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/yodaka/Desktop/jsStudy/ESModules/libs/functions.js from /Users/yodaka/Desktop/jsStudy/commonJS/main.js not supported. Instead change the require of functions.js in /Users/yodaka/Desktop/jsStudy/commonJS/main.js to a dynamic import() which is available in all CommonJS modules.だめ。 ESMのモジュールからCommonJSを呼ぶ ESModules/main.js import { hello } from './libs/functions.js' hello() import { hoge } from '../commonJS/libs/functions.js' hoge() ❯ node main.js hello 1234いける! と言うことでESModuleからCommonJSのモジュールをimportできる。 今後について前述した通り、ES Modulesが現在フロントエンド開発において主流のモジュール方式である。 が、まだCommonJSを捨てきれない理由がある。 ES Modulesに全振りする上での問題点 ES Modules方式のみを採用したライブラリを使いたい場合ここに書いてある対応をしないとそのライブラリは使えない。 https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm 具体的には プロジェクト自体をES Modulesにする(もっともおすすめ) "type": "module" を package.jsonに追加する package.jsonの"main": "index.js" を "exports": "./index.js"に置き換える. package.jsonの"engines"の項目をNode.js 12: "node": "^12.20.0 || ^14.13.1 || >=16.0.0"にあげる 'use strict'を全てのJavaScript fileから消す。 全てのrequire()/module.export をimport/exportに書き直す。 importは相対パスに書き換え、ファイル名をフルで書くようにする。(拡張子の省略すら許されない): import x from '.'; → import x from './index.js';. TypeScriptを使ってるなら型定義(index.d.ts)を ESM imports/exportsに変える.引用:https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-move-my-commonjs-project-to-esm 動的インポートを採用する import('../ESModules/libs/functions.js').then(functions2 => { functions2.hello() })以下も書けるが、awaitはasyncの中でしか使えないので普通にトップレベルのスコープで呼び出すとエラーになる。非同期で呼び出す必要がある。 const module = await import('../ESModules/libs/functions.js') functions2.hello() ❯ node main.js /Users/yodaka/Desktop/jsStudy/commonJS/main.js:3 const functions2 = await import('../ESModules/libs/functions.js') ^^^^^ SyntaxError: await is only valid in async functions and the top level bodies of modules at Object.compileFunction (node:vm:352:18)3. そのライブラリをCommonJS方式でモジュール化されているバージョンまで下げて使う。 普通に1が辛い正直webpackを使ってるならimport,export方式で書いてあるプロジェクトがほとんどだろうから、requireを書き換える作業はほぼないだろう。 しかし問題はここである。 importは相対パスに書き換え、ファイル名をフルで書くようにする。(拡張子の省略すら許されない): import x from '.'; → import x from './index.js';. 拡張子を省略しているプロジェクトは多いはず。全てに拡張子を書くのは正直現実的ではない。 さらにTypeScriptを採用しているプロジェクトはこれだけでは済まず追加で以下が必要になる。 tsconfig.jsonに"module": "ES2020"を追加 .tsファイルのimport時にも.jsの相対パス指定で呼び出す.tsを.jsとしてimportしないといけない。 個人的には気持ち悪いが、JavaScriptのセマンティクスをTypeScriptが変更しないようにというポリシーの結果と聞いて納得した。(以下の記事を参照) https://zenn.dev/teppeis/articles/2021-10-typescript-45-esm#識別子周りの議論 なんでES Modulesにしないといけないの?引用元 https://blog.sindresorhus.com/get-ready-for-esm-aa53530b3f77 ES Modulesは言語レベルの文法や、ブラウザサポート、デフォルトでstrictモード、非同期import、トップレベルでのawaitの使用、静的解析の向上、ツリーシェイキングなど様々な利点があります。 拙訳ですが、、 language-level syntaxがよくわからないまま言語レベルの文法と訳しました。 トップレベルでのawaitやstrictモードが標準だったり、静的解析が向上するのは嬉しい。 まとめ CommonJS 古株 Node.jsがデフォルトで採用 ES Modulesで書かれたモジュールは呼べない ES Modules 新しい ウェブブラウザがデフォルトで採用 Node.jsも最近のバージョンで対応済み CommonJSで書かれたモジュールが呼べる 全てをES Modulesにしていくために今まさに色々進められているTypeScriptの対応とともにどのライブラリも徐々にPureなES Modulesになって行くのではないかという印象がある。 いい機会だったのでまとめられてよかった。 最新情報や、間違っているところあればコメントいただけると幸いです。 |
CopyRight 2018-2019 实验室设备网 版权所有 |